import json
import os
import re
import numpy as np
import pandas as pd
import gradio as gr
from matplotlib import pyplot as plt
from scipy.signal import savgol_filter
from BaselineRemoval import BaselineRemoval


def input_data(file_path):
    with open(file_path, 'r') as file:
        data = json.load(file)

    fpga_signal = np.array([entry["fpga_signal"] for entry in data])
    wavelength = np.array([entry["wavelength"] for entry in data])

    return fpga_signal, wavelength

def savitzky_golay_filter(data:np.ndarray, window_length=15, polyorder=2, mode="nearest", deriv=0):
    '''
    SG滤波
    :param data: y值数组，np.ndarray
    :param window_length: 窗口长度，int，window_length的值越小，曲线越贴近真实曲线；window_length值越大，平滑效果越厉害（备注：该值必须为正奇整数）
    :param polyorder: polyorder为对窗口内的数据点进行k阶多项式拟合，k的值需要小于window_length。，int，k值越大，曲线越贴近真实曲线；k值越小，曲线平滑越厉害。另外，当k值较大时，受窗口长度限制，拟合会出现问题，高频曲线会变成直线。
    :param mode: 确定了要应用滤波器的填充信号的扩展类型。
    :return: 平滑后的矩阵，np.ndarray
    '''
    y = savgol_filter(data,window_length,polyorder,mode=mode,deriv=deriv)
    return y

def unit_vector_normalization(data: np.ndarray):
    '''单位矢量归一化

    :param data: 待归一化的y值数组，np.ndarray
    :return: 归一化后的y值数组，np.ndarray
    '''
    normalized_data = data / np.linalg.norm(data)
    return normalized_data

def polynomial_baseline_correction(data:np.ndarray, degree=3):
    '''
    对一维数据进行多项式基线校正
    :param data: 需要进行校正的一维数据数组。np.ndarray nxm
    :param degree: 用于拟合的多项式的次数。默认为3。int
    :return: 去除基线后的校正数据。 np.ndarray
    '''

    def baseline_correction(row, degree=3):
        '''
        接受一个一维数组并返回校正后的结果
        '''
        baseObj = BaselineRemoval(row.tolist())
        Modpoly_output = baseObj.ModPoly(degree)
        return np.array(Modpoly_output)

    corrected_data = np.array([baseline_correction(row, degree) for row in data])
    return corrected_data

def replace_outliers_with_next(matrix):
    '''
        剔除异常数据，并把它替换为右边一个元素数值（如果已经是最右边的元素，则替换为左边一个）
        :param matrix:np.ndarray nxm的光谱数据，n为数据个数，m为每个数据的光谱点数量
        :return:np.ndarray 剔除错误点并修改好了的光谱数据 nxm的光谱数据，n为数据个数，m为每个数据的光谱点数量
        '''
    def box_outliers(data):
        '''
        箱型图检测异常值
        :param data: pandas.series对象。待检测的一维光谱数据
        :return: pandas.series对象。修改后的一维数组
        '''
        # 对待检测的数据集进行排序
        sorted_data = data.sort_values()
        # 判断数据的总数量是奇数还是偶数
        if sorted_data.count() % 2 == 0:
            # 计算Q3，Q1,IQR
            Q3 = sorted_data[int(len(sorted_data) / 2):].median()
            Q1 = sorted_data[:int(len(sorted_data) / 2)].median()
        else:
            Q3 = sorted_data[int(len(sorted_data) / 2 - 1):].median()
            Q1 = sorted_data[:int(len(sorted_data) / 2 - 1)].median()
        IQR = round(Q3 - Q1, 1)
        # 获取1.5倍IQR的值
        upper_bound = round(Q3 + 1.5 * IQR, 1)
        lower_bound = round(Q1 - 1.5 * IQR, 1)
        # 判断哪些值是异常值
        rule = (data > upper_bound) | (data < lower_bound)
        # 获取异常值及其索引
        outliers = data[rule]
        # 如果检测到异常值，用相邻的非异常值替换
        if len(outliers) > 0:
            data[rule] = data.shift(-1)[rule]
            data[rule] = data[rule].fillna(data.iloc[-1])
        return data

    # 将矩阵转换为DataFrame，以便于处理每一行
    df = pd.DataFrame(matrix)
    # 遍历每一行
    for i in range(df.shape[0]):
        # 应用box_outliers函数检测异常值
        box_outliers(df.iloc[i])
        # 获取右侧的数据，如果右侧没有数据，则使用最后一列的数据
        right_data = df.iloc[i].shift(-1)
        right_data = right_data.fillna(df.iloc[i].iloc[-1])  # 如果最后一列是NaN，则替换为最后一列的数据
        #print(f"右边的元素为：{right_data}")
        # 用右侧数据替换异常值
        df.iloc[i] = df.iloc[i].where(~df.iloc[i].isna(), right_data)
        #df.iloc[i].where(df.iloc[i].isin(outliers), right_data, inplace=True)
    return df.values

def pre_single_display(file_path, pre_fun):
    if (file_path != None):
        origin_data, wavelength = input_data(file_path)
        data = origin_data
        deal_flag = False
        while pre_fun:
            deal_flag = True
            cur_fun = pre_fun.pop(0)
            if cur_fun == "SG滤波":
                data = savitzky_golay_filter(data)
            if cur_fun == "归一化":
                data = unit_vector_normalization(data)
            if cur_fun == "基线校正":
                data = polynomial_baseline_correction(data)
            if cur_fun == "异常值剔除":
                data = replace_outliers_with_next(data)

        if deal_flag:
            df = pd.DataFrame({
                'wavelength': np.append(wavelength,wavelength),
                'data': np.append(origin_data,data),
                'type':["origin_data"]* origin_data.size + ["data"]* data.size
            })
        else:
            df = pd.DataFrame({
                'wavelength': wavelength,
                'data':origin_data,
                'type': ["origin_data"] * origin_data.size
            })

        update = gr.LinePlot(
            value=df,
            x="wavelength",
            y="data",
            title="光谱数据",
            width=600,
            height=350,
            color="type",
        )
        return update
    else:
        gr.Warning("未选择文件")
        print("未输入文件路径")
